package com.ls.fw.web.core.server.tomcat;


import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.FileSystems;
import java.nio.file.Files;

import javax.servlet.ServletException;

import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.Service;
import org.apache.catalina.WebResourceRoot;
import org.apache.catalina.WebResourceSet;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.webresources.DirResourceSet;
import org.apache.catalina.webresources.EmptyResourceSet;
import org.apache.catalina.webresources.StandardRoot;
import org.apache.commons.lang3.StringUtils;

import com.ls.fw.web.core.server.tomcat.core.FwStandardContext;
import com.ls.fw.web.core.server.tomcat.core.FwStandardJarScanner;
import com.ls.fw.web.core.server.tomcat.core.Tomcat;

/**
 * Tomcat嵌入服务
 * 
 * @author lisheng
 * @date 2016年3月13日 下午3:31:12
 * @version V1.0
 */
public class CustomTomcatServer extends Tomcat{
	private String xmlConfigPath;

	private String contextPath = "/";

	private String warPath;

	private int sslPort = 8443;
	private int port = 80;

	private String webAppDir = "src/main/webapp";

	private String webXmlPath = "src/main/webapp/WEB-INF/web.xml";

	private String keyAlias;
	private String storePath;
	private String password;
	//org.apache.coyote.http11.Http11NioProtocol
	private String protocol = "HTTP/1.1";
	  /** 
     * 服务是否正在启用状态中 
     */  
    private boolean isRunning;
   /** 
    * @Fields isServlet3Enable: 是否启用servlet 3.0 支持，如果启用的话，就需要扫描jar包中是否有Servlet等annotation，这个会影响启动时间，默认不开启
    */
    private boolean isServlet3Enable = true;

    private boolean scanAnnotations = false;
	
	public CustomTomcatServer() {
		super();
	}

	public CustomTomcatServer( String contextPath, int port) {
		super();
		this.port = port;
		this.contextPath = contextPath;
	}

	public String getXmlConfigPath() {
		return this.xmlConfigPath;
	}

	public CustomTomcatServer setXmlConfigPath(String xmlConfigPath) {
		this.xmlConfigPath = xmlConfigPath;
		return this;
	}

	public String getContextPath() {
		return this.contextPath;
	}

	public CustomTomcatServer setContextPath(String contextPath) {
		this.contextPath = contextPath;
		return this;
	}

	public String getWarPath() {
		return this.warPath;
	}

	public CustomTomcatServer warPath(String warPath) {
		this.warPath = warPath;
		return this;
	}

	public int getPort() {
		return this.port;
	}

	public CustomTomcatServer port(int port) {
		this.port = port;
		return this;
	}

	public String getWebAppDir() {
		return this.webAppDir;
	}

	public CustomTomcatServer setWebAppDir(String webAppDir) {
		this.webAppDir = webAppDir;
		return this;
	}

	public String getWebXmlPath() {
		return this.webXmlPath;
	}

	public CustomTomcatServer setWebXmlPath(String webXmlPath) {
		this.webXmlPath = webXmlPath;
		return this;
	}

	protected void doStart() throws Exception {
		if (StringUtils.isNotBlank(warPath)) {
             applyHandle(true);
         } else {
             applyHandle(false);
         }
		 this.start();
	}
	
	 /** 
     * 重启Tomcat服务 
     */  
    public void restartTomcat(){  
        try {  
            if(isRunning){  
                this.stop();  
                startServer();  
            }else {  
            	startServer();  
            }  
        } catch (LifecycleException e) {  
            e.printStackTrace();  
        }  
    }  
	
	/**
	 * 启动服务
	 * @author lisheng
	 * @date 2016年3月13日 下午3:53:50
	 * @version V1.0
	 * @throws Exception
	 */
	public void startServer(){
		try {
			long begin = System.currentTimeMillis();
			this.doStart();
			isRunning = true;  
			long end = System.currentTimeMillis();
            log(end - begin);
			this.join();
			this.getServer().await();
		} catch (Exception e1) {
	        e1.printStackTrace();
	        System.err.println("服务挂了... ");
			System.exit(-1);
		}
	}

	protected void applyHandle(Boolean warDeployFlag) throws ServletException, IOException {
		this.setBaseDir("target");
		this.setPort(port);
        Connector connector = new Connector(protocol);
        connector.setPort(port);
        connector.setURIEncoding("utf-8");
        this.setConnector(connector);
        if(StringUtils.isNotBlank(storePath)
        		&& StringUtils.isNotBlank(password)
        		&& StringUtils.isNotBlank(keyAlias)){
    	   Service service = this.getService();
           service.addConnector(getHttpsConnector());
        }
        this.getService().addConnector(connector);
        this.getService().addLifecycleListener(new LifecycleListener() {  
            public void lifecycleEvent(LifecycleEvent le) {  
                String lc = le.getType();  
                if(lc.equals(Lifecycle.START_EVENT)){  
                    System.out.println("starting!");  
                    isRunning = true;  
                }else if (lc.equals(Lifecycle.STOP_EVENT)){  
                    System.out.println("stop!");  
                    isRunning = false;  
                } 
            }  
        });  
        String webappPath = getWebAppDir();
//        File root = getRootFolder();
        File webContentFolder = new File(webappPath);//new File(root.getAbsolutePath(), webappPath);
        if (!webContentFolder.exists()) {
            webContentFolder = Files.createTempDirectory("default-doc-base").toFile();
        }
        
        //Add Servlet 3.0
        FwStandardContext ctx = (FwStandardContext) this.addWebapp(contextPath, webContentFolder.getAbsolutePath());
//        ctx.setLogEffectiveWebXml(true);
        //Set execution independent of current thread context classloader (compatibility with exec:java mojo)
//        ctx.setParentClassLoader(CustomTomcatServer.class.getClassLoader());
        FwStandardJarScanner scanner = (FwStandardJarScanner) ctx.getJarScanner();
        if (!isServlet3Enable) {
            scanner.setScanAllDirectories(false);
            scanner.setScanClassPath(false);
        }else{
        	 scanner.setScanAllDirectories(true);
             scanner.setScanClassPath(true);
        }
        System.setProperty("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE", "true");
        //Disable TLD scanning by default
//        if (System.getProperty(Constants.SKIP_JARS_PROPERTY) == null && System.getProperty(Constants.SKIP_JARS_PROPERTY) == null) {
//            System.out.println("disabling TLD scanning");
//            StandardJarScanFilter jarScanFilter = (StandardJarScanFilter) ctx.getJarScanner().getJarScanFilter();
//            jarScanFilter.setTldSkip("*");
//        }
        System.out.println("configuring app with basedir: " + webContentFolder.getAbsolutePath());
        // Declare an alternative location for your "WEB-INF/classes" dir
        // Servlet 3.0 annotation will work
        File additionWebInfClassesFolder = FileSystems.getDefault().getPath("target", "classes").toFile();
//        File additionWebInfClassesFolder = new File(root.getAbsolutePath(), "target/classes");
        WebResourceRoot resources = new StandardRoot(ctx);
        WebResourceSet resourceSet;
        if (additionWebInfClassesFolder.exists()) {
            resourceSet = new DirResourceSet(resources, "/WEB-INF/classes", additionWebInfClassesFolder.getAbsolutePath(), "/");
            System.out.println("loading WEB-INF resources from as '" + additionWebInfClassesFolder.getAbsolutePath() + "'");
        } else {
            resourceSet = new EmptyResourceSet(resources);
        }
        resources.addPreResources(resourceSet);
        ctx.addLifecycleListener(new Tomcat.FixContextListener());
        //ctx.setSessionCookieName("t-session");
        ctx.setResources(resources);
        this.getHost().setAppBase(additionWebInfClassesFolder.getAbsolutePath());
        this.setSilent(true);
        Connector[] connectors = this.getService().findConnectors();
        for (Connector connector1 : connectors) {
            System.out.println("Protocol Handler: " + connector1.getProtocolHandlerClassName() + " port: " + connector1.getPort());
        }
	}
	
	private Connector getHttpsConnector(){
	     Connector httpsConnector = new Connector();
        httpsConnector.setPort(8443);
        httpsConnector.setSecure(true);
        httpsConnector.setScheme("https");
        httpsConnector.setAttribute("keyAlias", this.keyAlias );
        httpsConnector.setAttribute("keystorePass", password);
        httpsConnector.setAttribute("keystoreFile", FileSystems.getDefault().getPath(storePath).toFile().getAbsolutePath());
        httpsConnector.setAttribute("clientAuth", "false");
        httpsConnector.setAttribute("sslProtocol", "TLS");
        httpsConnector.setAttribute("SSLEnabled", true);
        return httpsConnector;
	}
	
	 private static File getRootFolder() {
	        try {
	            File root;
	            String runningJarPath = CustomTomcatServer.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath().replaceAll("\\\\", "/");
	            int lastIndexOf = runningJarPath.lastIndexOf("/target/");
	            if (lastIndexOf < 0) {
	                root = new File("");
	            } else {
	                root = new File(runningJarPath.substring(0, lastIndexOf));
	            }
	            System.out.println("application resolved root folder: " + root.getAbsolutePath());
	            return root;
	        } catch (URISyntaxException ex) {
	            throw new RuntimeException(ex);
	        }
	    }
	 
	public static void main(String[] args) throws Exception {
		CustomTomcatServer jetty = new CustomTomcatServer("/test", 80 );
//			jetty.setStorePath("D:\\server\\tomcat\\ssl\\lisheng.keystore");
//			jetty.setPassword("lisheng");
		jetty.startServer();
	}
	 private void log(long time) {
	        System.out.println("********************************************************");
	        System.out.println("启动成功: http://127.0.0.1:" + port + "   in:" + time + "ms");
	        System.out.println("您可以直接在控制台里敲回车，重启tomcat。");
	        System.out.println("********************************************************");
	    }
	public String getStorePath() {
		return storePath;
	}

	public CustomTomcatServer setStorePath(String storePath) {
		this.storePath = storePath;
		return this;
	}

	public int getSslPort() {
		return this.sslPort;
	}

	public CustomTomcatServer setSslPort(int sslPort) {
		this.sslPort = sslPort;
		return this;
	}

	public String getPassword() {
		return password;
	}

	public CustomTomcatServer setPassword(String password) {
		this.password = password;
		return this;
	}

	public String getProtocol() {
		return protocol;
	}

	public CustomTomcatServer setProtocol(String protocol) {
		this.protocol = protocol;
		return this;
	}

	public void join() throws InterruptedException {
		Thread t = new Thread(new Runnable() {
			
			@Override
			public void run() {
				System.out.println("[HINT] Hit Enter to reload the application quickly");
				try {
					// 等待用户输入回车重载应用.
					while (true) {
					    char c = (char) System.in.read();
					    if (c == '\n') {
		                    System.out.println("重启tomcat...");
		                    stop();
		                    isRunning = false;
		                    startServer();
					        System.out.println("[HINT] Hit Enter to reload the application quickly");
					     }
					}
				} catch (Exception e) {
					e.printStackTrace();
					System.exit(-1);
				}
			}
		});
		t.setDaemon(true);
		t.start();
	}

	public boolean isScanAnnotations() {
		return this.scanAnnotations;
	}

	public CustomTomcatServer setScanAnnotations(boolean scanAnnotations) {
		this.scanAnnotations = scanAnnotations;
		return this;
	}
}